//---------------------------------------------------------------------------
//    RobotSee Interpreter - Written by Eric Gregori ( www.EMGRobotics.com )
//    Copyright (C) 2010  Eric Gregori
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.
//---------------------------------------------------------------------------
//
//*********************************************************************************************************************8
// Robot Vision Toolkit by Eric Gregori
//
// Wrapper for a threaded RobotVisionToolkit Language Extension
//
//*********************************************************************************************************************8
#include "serial_port.h"

//********************************************************************************
// Private
//********************************************************************************
void Serial_Port::Serial_Close( void )
{
	CloseHandle( Handle );
}


int  Serial_Port::SetSerialConfiguration( void )
{
    COMMTIMEOUTS   timeouts ;
    DCB            dcb      ;

    //                                                                     //
    // Get the current timeout values for the SERIAL port.                 //
    //                                                                     //
    
    if ( ! GetCommTimeouts( Handle , &timeouts ) )
    {
        return( 1 ) ;
    }

    //                                                                     //
    // Set the timeouts so the READ operations are NON-BLOCKING.           //
    //                                                                     //

    timeouts.ReadIntervalTimeout        = MAXDWORD ;
    timeouts.ReadTotalTimeoutMultiplier = 0        ;
    timeouts.ReadTotalTimeoutConstant   = 0        ;

    if ( ! SetCommTimeouts( Handle , &timeouts ) )
    {
        return( 2 ) ;
    }

    //                                                                     //
    // Get the current communications state.                               //
    //                                                                     //
    
    if ( GetCommState( Handle , &dcb ) == FALSE )
    {
        return( 3 ) ;
    }

    //                                                                     //
    // Set the baud rate.                                                  //
    //                                                                     //
    
	dcb.BaudRate	 = Baud;
    dcb.Parity       = (BYTE) 0;
    dcb.StopBits     = (BYTE) 0;
    dcb.fOutxCtsFlow = 0;
    dcb.fOutxDsrFlow = 0;

	if( flags & 0x01 )
		dcb.fDtrControl  = DTR_CONTROL_ENABLE;
	else
		dcb.fDtrControl  = DTR_CONTROL_DISABLE;

	if( flags & 0x02 )
		dcb.fRtsControl	 = RTS_CONTROL_ENABLE;
	else
		dcb.fRtsControl	 = RTS_CONTROL_DISABLE;

    dcb.ByteSize     = (BYTE) 8;
    dcb.fBinary      = 1;
    dcb.fAbortOnError = 0;

	if ( SetCommState( Handle , &dcb ) == FALSE )
    {
        return( 4 ) ;
    }

	printf( "\nSerial Port initialization Complete - port = %d", Port );
	printf( "\nBaudRate = %d, Bits = %d, Stop = %d", 
			dcb.BaudRate, 
			dcb.ByteSize, 
			dcb.StopBits 
		  );
	printf( "\nfBinary = %lx, fParity = %lx, fOutX = %lx, fInX = %lx",
			dcb.fBinary, 
			dcb.fParity, 
			dcb.fOutX, 
			dcb.fInX
		  );
	printf( "\nfOutxCtsFlow = %lx, fOutxDsrFlow = %lx, fDtrControl = %lx",
			dcb.fOutxCtsFlow,
			dcb.fOutxDsrFlow,
			dcb.fDtrControl
		  );
	printf( "\nfDsrSensitivity = %lx, fErrorChar = %lx, fNull = %lx",
			dcb.fDsrSensitivity,
			dcb.fErrorChar,
			dcb.fNull
		  );
	printf( "\nfRtsControl = %lx, fAbortOnError = %lx, XonLim = %lx",
			dcb.fRtsControl,
			dcb.fAbortOnError,
			dcb.XonLim
		  );
	printf( "\nfAbortOnError = %lx, XonLim = %lx, XoffLim = %lx",
			dcb.fAbortOnError,
			dcb.XonLim,
			dcb.XoffLim
		  );
	printf( "\nXonChar = %lx, XoffChar = %lx, ErrorChar = %lx",
			dcb.XonChar,
			dcb.XoffChar,
			dcb.ErrorChar
		  );
	printf( "\nEofChar = %lx, EvtChar = %lx",
			dcb.EofChar, dcb.EvtChar 
		  );

    return( 0 ) ;
}


int Serial_Port::Serial_Init( void )
{
	wchar_t filename[16];

	wsprintf( filename, L"COM%d:", Port );

	Handle = CreateFile(	filename,                    
							GENERIC_READ | GENERIC_WRITE,
                            0,								// exclusive 
                            NULL,							// no security
                            OPEN_EXISTING,
                            FILE_ATTRIBUTE_NORMAL,
                            NULL                       ) ;

	if ( Handle != INVALID_HANDLE_VALUE )
	{
		if( !SetSerialConfiguration( ) )
		{
			return(0);
		}
		else
		{
			printf( "\nFAILURE - SetSerialConfiguration" );
			CloseHandle( Handle );
			return(1);
		}
	}
	else
	{
		printf( "\nFailure - CreateFile" );
		return(2);
	}
}


//********************************************************************************
// Public
//********************************************************************************
int Serial_Port::Serial_Read( DWORD buffer_size, char *data )
{
    unsigned long   nRead                            ;
        
	if( !Portup ) return( -1 );
    
    //                                                                     //
    // Loop until we are told to stop.                                     //
    //                                                                     //

    nRead = 0 ;

	ReadFile	( 
					Handle,
					data,
					buffer_size,
					&nRead,
					NULL 
				);

	return( nRead );
}


int Serial_Port::Serial_Send( const char * s , const int n )
{
    DWORD   nWritten ;

	if( !Portup ) return( -1 );

    //                                                                     //
    // Perform a NON-BLOCKING write.                                       //
    //                                                                     //

    if ( ! WriteFile( Handle , s , (DWORD) n , &nWritten , NULL ) )
    {
       return( -1 ) ;
    }

    if ( nWritten == 0 )
    {
       Sleep( 10 ) ;
    }

    //                                                                     //
    // Return the number of bytes which were WRITTEN.                      //
    //                                                                     //

    return( (int) nWritten ) ;
}


void Serial_Port::Serial_Reset( void )
{
	if( Portup )
		(void)PurgeComm( Handle, PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR );
}

// Constructor
Serial_Port::Serial_Port( int l_baud=9600, int l_port=1, int l_flags=0 )
{
	Portup = 0;
	Port   = l_port;
	Baud   = l_baud; 
	flags  = l_flags;

	if( Serial_Init() == 0 )
	{
		Portup = 1;
	}
}

// Destructor
Serial_Port::~Serial_Port(void)
{
	if( Portup )
		Serial_Close();
}


int Serial_Test( int Port )
{
	HANDLE	Handle;
	wchar_t filename[16];

	wsprintf( filename, L"COM%d:", Port );

	Handle = CreateFile(	filename,                    
							GENERIC_READ | GENERIC_WRITE,
                            0,								// exclusive 
                            NULL,							// no security
                            OPEN_EXISTING,
                            FILE_ATTRIBUTE_NORMAL,
                            NULL                       ) ;

	if ( Handle != INVALID_HANDLE_VALUE )
	{
		CloseHandle( Handle );
		return(1);
	}

	return(0);
}